﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Mail;
using System.Security.Cryptography;
using System.IO;
using System.Data.SqlClient;
/*using Oracle.DataAccess.Client;*/

namespace Landscape.Business.Common
{
    public static class Utils
    {
        // Define default min and max password lengths.
        private static int DEFAULT_MIN_PASSWORD_LENGTH = 8;
        private static int DEFAULT_MAX_PASSWORD_LENGTH = 10;

        // Define supported password characters divided into groups.
        // You can add (or remove) characters to (from) these groups.
        private static string PASSWORD_CHARS_LCASE = "abcdefgijkmnopqrstwxyz";
        private static string PASSWORD_CHARS_UCASE = "ABCDEFGHJKLMNPQRSTWXYZ";
        private static string PASSWORD_CHARS_NUMERIC = "23456789";
        private static string PASSWORD_CHARS_SPECIAL = "*$_&!%#@^";

        private static string EMailTemplate
        {
            get
            {
                return @"<table style='border: thin solid #000000; padding: 0px; margin: 0px; font-family:Verdana; width:443px' cellspacing='0px' cellpadding='0px'>
                        <tr style='background-color: #B8DBFF; text-align: center; vertical-align: middle; height:70px'>
                        <td style='width:100px'><img alt='OmniLogo' src='http://www.omnioffshore.com/Images/Omni-Small32.png' style='width: 45px; height: 32px;' />
                        </td><td><h2>Omni Offshore Terminals</h2></td></tr><tr>
                        <td colspan='2' style='text-align: center; vertical-align: middle; font-style:oblique'><p>{0}</p></td></tr><tr>
                        <td colspan='2' style='text-align: left; vertical-align: top; font-size:13px; min-height:200px'><p>{1}</p></td></tr>
                        <tr style='background-color: #F099A2'>
                        <td colspan='2' style='font-size:smaller; text-align:left; vertical-align:top; height:70px'>                
                        <b>NOTE: This is an autogenerated email. Please do not reply.</b>
                        </td></tr></table>";
            }
        }

        /// <summary>
        /// Generates a random password.
        /// </summary>
        /// <returns>
        /// Randomly generated password.
        /// </returns>
        /// <remarks>
        /// The length of the generated password will be determined at
        /// random. It will be no shorter than the minimum default and
        /// no longer than maximum default.
        /// </remarks>
        public static string GeneratePassword()
        {
            return GeneratePassword(DEFAULT_MIN_PASSWORD_LENGTH,
                            DEFAULT_MAX_PASSWORD_LENGTH);
        }

        /// <summary>
        /// Generates a random password of the exact length.
        /// </summary>
        /// <param name="length">
        /// Exact password length.
        /// </param>
        /// <returns>
        /// Randomly generated password.
        /// </returns>
        public static string GeneratePassword(int length)
        {
            return GeneratePassword(length, length);
        }

        /// <summary>
        /// Generates a random password.
        /// </summary>
        /// <param name="minLength">
        /// Minimum password length.
        /// </param>
        /// <param name="maxLength">
        /// Maximum password length.
        /// </param>
        /// <returns>
        /// Randomly generated password.
        /// </returns>
        /// <remarks>
        /// The length of the generated password will be determined at
        /// random and it will fall with the range determined by the
        /// function parameters.
        /// </remarks>
        public static string GeneratePassword(int minLength, int maxLength)
        {
            // Make sure that input parameters are valid.
            if (minLength <= 0 || maxLength <= 0 || minLength > maxLength)
                return null;

            // Create a local array containing supported password characters
            // grouped by types. You can remove character groups from this
            // array, but doing so will weaken the password strength.
            char[][] charGroups = new char[][] 
        {
            PASSWORD_CHARS_LCASE.ToCharArray(),
            PASSWORD_CHARS_UCASE.ToCharArray(),
            PASSWORD_CHARS_NUMERIC.ToCharArray(),
            PASSWORD_CHARS_SPECIAL.ToCharArray()
        };

            // Use this array to track the number of unused characters in each
            // character group.
            int[] charsLeftInGroup = new int[charGroups.Length];

            // Initially, all characters in each group are not used.
            for (int i = 0; i < charsLeftInGroup.Length; i++)
                charsLeftInGroup[i] = charGroups[i].Length;

            // Use this array to track (iterate through) unused character groups.
            int[] leftGroupsOrder = new int[charGroups.Length];

            // Initially, all character groups are not used.
            for (int i = 0; i < leftGroupsOrder.Length; i++)
                leftGroupsOrder[i] = i;

            // Because we cannot use the default randomizer, which is based on the
            // current time (it will produce the same "random" number within a
            // second), we will use a random number generator to seed the
            // randomizer.

            // Use a 4-byte array to fill it with random bytes and convert it then
            // to an integer value.
            byte[] randomBytes = new byte[4];

            // Generate 4 random bytes.
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            rng.GetBytes(randomBytes);

            // Convert 4 bytes into a 32-bit integer value.
            int seed = (randomBytes[0] & 0x7f) << 24 |
                        randomBytes[1] << 16 |
                        randomBytes[2] << 8 |
                        randomBytes[3];

            // Now, this is real randomization.
            Random random = new Random(seed);

            // This array will hold password characters.
            char[] password = null;

            // Allocate appropriate memory for the password.
            if (minLength < maxLength)
                password = new char[random.Next(minLength, maxLength + 1)];
            else
                password = new char[minLength];

            // Index of the next character to be added to password.
            int nextCharIdx;

            // Index of the next character group to be processed.
            int nextGroupIdx;

            // Index which will be used to track not processed character groups.
            int nextLeftGroupsOrderIdx;

            // Index of the last non-processed character in a group.
            int lastCharIdx;

            // Index of the last non-processed group.
            int lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;

            // Generate password characters one at a time.
            for (int i = 0; i < password.Length; i++)
            {
                // If only one character group remained unprocessed, process it;
                // otherwise, pick a random character group from the unprocessed
                // group list. To allow a special character to appear in the
                // first position, increment the second parameter of the Next
                // function call by one, i.e. lastLeftGroupsOrderIdx + 1.
                if (lastLeftGroupsOrderIdx == 0)
                    nextLeftGroupsOrderIdx = 0;
                else
                    nextLeftGroupsOrderIdx = random.Next(0,
                                                         lastLeftGroupsOrderIdx);

                // Get the actual index of the character group, from which we will
                // pick the next character.
                nextGroupIdx = leftGroupsOrder[nextLeftGroupsOrderIdx];

                // Get the index of the last unprocessed characters in this group.
                lastCharIdx = charsLeftInGroup[nextGroupIdx] - 1;

                // If only one unprocessed character is left, pick it; otherwise,
                // get a random character from the unused character list.
                if (lastCharIdx == 0)
                    nextCharIdx = 0;
                else
                    nextCharIdx = random.Next(0, lastCharIdx + 1);

                // Add this character to the password.
                password[i] = charGroups[nextGroupIdx][nextCharIdx];

                // If we processed the last character in this group, start over.
                if (lastCharIdx == 0)
                    charsLeftInGroup[nextGroupIdx] =
                                              charGroups[nextGroupIdx].Length;
                // There are more unprocessed characters left.
                else
                {
                    // Swap processed character with the last unprocessed character
                    // so that we don't pick it until we process all characters in
                    // this group.
                    if (lastCharIdx != nextCharIdx)
                    {
                        char temp = charGroups[nextGroupIdx][lastCharIdx];
                        charGroups[nextGroupIdx][lastCharIdx] =
                                    charGroups[nextGroupIdx][nextCharIdx];
                        charGroups[nextGroupIdx][nextCharIdx] = temp;
                    }
                    // Decrement the number of unprocessed characters in
                    // this group.
                    charsLeftInGroup[nextGroupIdx]--;
                }

                // If we processed the last group, start all over.
                if (lastLeftGroupsOrderIdx == 0)
                    lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;
                // There are more unprocessed groups left.
                else
                {
                    // Swap processed group with the last unprocessed group
                    // so that we don't pick it until we process all groups.
                    if (lastLeftGroupsOrderIdx != nextLeftGroupsOrderIdx)
                    {
                        int temp = leftGroupsOrder[lastLeftGroupsOrderIdx];
                        leftGroupsOrder[lastLeftGroupsOrderIdx] =
                                    leftGroupsOrder[nextLeftGroupsOrderIdx];
                        leftGroupsOrder[nextLeftGroupsOrderIdx] = temp;
                    }
                    // Decrement the number of unprocessed groups.
                    lastLeftGroupsOrderIdx--;
                }
            }

            // Convert password characters into a string and return the result.
            return new string(password);
        }

        public static Guid GetGUID()
        {
            Byte[] initBytes = new Byte[16];
            Byte[] timeTicks = BitConverter.GetBytes(DateTime.Now.Ticks);
            Random randNo = new Random();

            for (int idx = 0; idx < 8; idx++)
            {
                initBytes[idx] = timeTicks[idx];
                initBytes[idx + 8] = Convert.ToByte(randNo.Next(1, 255));
            }

            return new Guid(initBytes);
        }

        public static string GetConnectionString()
        {
            string _connectionString = string.Empty;

#if DEBUG
            _connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["LANDSCAPE_DEBUG"].ConnectionString;
#else
            _connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["TPOTDB_RELEASE"].ConnectionString;
#endif

            return _connectionString;
        }

        public static string GetConnectionStringTagName()
        {
            string _connectionString = string.Empty;

#if DEBUG
            _connectionString = "TPOTDB_DEBUG";
#else
            _connectionString = "TPOTDB_RELEASE";
#endif

            return _connectionString;
        }

        public static void SendMail(string pToMailID, string pCCEmailID, string pBCCEmailID, string pSubject, string pBody, Dictionary<string, string> pPlacementData, string pModuleName = "", bool pHtmlBody = true)
        {
            if (pToMailID == null || pToMailID.Trim() == string.Empty)
            {
                throw new LandscapeException("Please assign the email ids to send the mail");
            }

            if (pSubject == null || pSubject.Trim() == string.Empty)
            {
                throw new LandscapeException("Please assign the subject to send the mail");
            }

            if (pBody == null || pBody.Trim() == string.Empty)
            {
                throw new LandscapeException("Please assign the email body to send the mail");
            }

            MailMessage toBeSentMail = new MailMessage();

            Task loadTo = new Task(() =>
            {
                if (pToMailID.Contains(';'))
                {
                    foreach (string mailID in pToMailID.Split(';'))
                    {
                        if (mailID.Trim() != string.Empty)
                            toBeSentMail.To.Add(new MailAddress(mailID));
                    }
                }
                else
                {
                    toBeSentMail.To.Add(new MailAddress(pToMailID));
                }
            });
            loadTo.Start();

            Task loadCC = new Task(() =>
            {
                if (pCCEmailID != null && pCCEmailID.Trim() != string.Empty)
                {
                    if (pCCEmailID.Contains(';'))
                    {
                        foreach (string mailID in pCCEmailID.Split(';'))
                        {
                            if (mailID.Trim() != string.Empty)
                                toBeSentMail.CC.Add(new MailAddress(mailID));
                        }
                    }
                    else
                    {
                        toBeSentMail.CC.Add(new MailAddress(pCCEmailID));
                    }
                }                
            });
            loadCC.Start();

            Task loadBCC = new Task(() =>
            {
                if (pBCCEmailID != null && pBCCEmailID.Trim() != string.Empty)
                {
                    if (pBCCEmailID.Contains(';'))
                    {
                        foreach (string mailID in pBCCEmailID.Split(';'))
                        {
                            if (mailID.Trim() != string.Empty)
                                toBeSentMail.Bcc.Add(new MailAddress(mailID));
                        }
                    }
                    else
                    {
                        toBeSentMail.Bcc.Add(new MailAddress(pBCCEmailID));
                    }
                }
            });
            loadBCC.Start();

            Task loadBody = new Task(() =>
            {
                if (pPlacementData != null)
                {
                    foreach (var item in pPlacementData)
                    {
                        if (pBody.Contains(item.Key))
                        {
                            string oldItem = "{" + item.Key + "}";
                            pBody = pBody.Replace(oldItem, item.Value);
                        }
                    }
                    toBeSentMail.Body = pBody;
                }
                else
                {
                    toBeSentMail.Body = pBody;
                }
            });
            loadBody.Start();

            Task loadSubject = new Task(() =>
            {
                if (pPlacementData != null)
                {
                    foreach (var item in pPlacementData)
                    {
                        if (pSubject.Contains(item.Key))
                        {
                            string oldItem = "{" + item.Key + "}";
                            pSubject = pSubject.Replace(oldItem, item.Value);
                        }
                        toBeSentMail.Subject = pSubject;
                    }
                }
                else
                {
                    toBeSentMail.Body = string.Format(EMailTemplate, pModuleName, pBody);
                }

            });
            loadSubject.Start();

            loadTo.Wait();
            loadCC.Wait();
            loadBCC.Wait();
            loadBody.Wait();
            loadSubject.Wait();

            toBeSentMail.IsBodyHtml = pHtmlBody;
            toBeSentMail.Subject = pSubject;

            SmtpClient mailClient = new SmtpClient();
            mailClient.Send(toBeSentMail);
        }

        private static DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider();

        /// <summary>
        /// Encrypt a string.
        /// </summary>
        /// <param name="pOriginalString">The original string.</param>
        /// <returns>The encrypted string.</returns>
        /// <exception cref="ArgumentNullException">This exception will be 
        /// thrown when the original string is null or empty.</exception>
        public static string Encrypt(string PlainText)
        {
            // Create a memory stream.
            MemoryStream ms = new MemoryStream();

            // Create a CryptoStream using the memory stream and the 
            // CSP DES key.  
            CryptoStream encStream = new CryptoStream(ms, cryptoProvider.CreateEncryptor(), CryptoStreamMode.Write);

            // Create a StreamWriter to write a string
            // to the stream.
            StreamWriter sw = new StreamWriter(encStream);

            // Write the plaintext to the stream.
            sw.WriteLine(PlainText);

            // Close the StreamWriter and CryptoStream.
            sw.Close();
            encStream.Close();

            // Get an array of bytes that represents
            // the memory stream.
            byte[] buffer = ms.ToArray();

            // Close the memory stream.
            ms.Close();

            // Return the encrypted byte array.
            return Convert.ToBase64String(buffer);
        }

        /// <summary>
        /// Decrypt a crypted string.
        /// </summary>
        /// <param name="pCryptedString">The crypted string.</param>
        /// <returns>The decrypted string.</returns>
        /// <exception cref="ArgumentNullException">This exception will be thrown 
        /// when the crypted string is null or empty.</exception>
        public static string Decrypt(string CypherText)
        {
            string[] parts = CypherText.Split(' ');
            string finalString = string.Empty;
            for (int idx = 0; idx < parts.Length; idx++)
            {
                if (idx != 0)
                {
                    finalString += "+";
                }
                finalString += parts[idx];
            }
            // Create a memory stream to the passed buffer.
            MemoryStream ms = new MemoryStream(Convert.FromBase64String(finalString));

            // Create a CryptoStream using the memory stream and the 
            // CSP DES key. 
            CryptoStream encStream = new CryptoStream(ms, cryptoProvider.CreateDecryptor(), CryptoStreamMode.Read);

            // Create a StreamReader for reading the stream.
            StreamReader sr = new StreamReader(encStream);

            // Read the stream as a string.
            string val = sr.ReadLine();

            // Close the streams.
            sr.Close();
            encStream.Close();
            ms.Close();

            return val;
        }

        public static string Base64Encrypt(string pString)
        {
            byte[] bytes = System.Text.ASCIIEncoding.ASCII.GetBytes(pString);
            return System.Convert.ToBase64String(bytes);
        }

        public static string Base64Decrypt(string pString)
        {
            byte[] bytes = System.Convert.FromBase64String(pString);
            return System.Text.ASCIIEncoding.ASCII.GetString(bytes);
        }

        public static int GetActionTrackSequenceNum()
        {
            using (SqlConnection oCon = new SqlConnection(GetConnectionString()))
            {
                using (SqlCommand oCmd = new SqlCommand())
                {
                    string cmd = "SELECT SEQ_CIA_NO.NEXTVAL FROM DUAL";

                    oCmd.Connection = oCon;
                    oCmd.CommandText = cmd;
                    oCmd.CommandType = System.Data.CommandType.Text;

                    oCon.Open();
                    var seq = Convert.ToInt32(oCmd.ExecuteScalar());

                    oCmd.Dispose();
                    oCon.Close();

                    return seq;
                }
            }
        }

        public static void CheckAndResetSequence()
        {
            string getRecordCount = "SELECT COUNT(*) FROM ACTION WHERE TO_CHAR(RELEASED_DATE, 'YYYY') = TO_CHAR(CURRENT_DATE, 'YYYY')";
            string simpleSelect = "SELECT SEQ_CIA_NO.NEXTVAL FROM DUAL";
            string getSubComponent = "SELECT SEQ_CIA_NO.CURRVAL FROM DUAL";
            string aleterCmd = "ALTER SEQUENCE SEQ_CIA_NO INCREMENT BY -{0} MINVALUE 0";
            string resetIncCmd = "ALTER SEQUENCE SEQ_CIA_NO INCREMENT BY 1";

            using (SqlConnection oCon = new SqlConnection(GetConnectionString()))
            {
                using (SqlCommand oCmd = new SqlCommand())
                {
                    oCon.Open();
                    oCmd.Connection = oCon;
                    oCmd.CommandType = System.Data.CommandType.Text;

                    //Get the count of records who having release date is in the current year
                    oCmd.CommandText = getRecordCount;
                    var recCount = Convert.ToInt32(oCmd.ExecuteScalar());
                    if (recCount == 0)
                    {//If no records exists for the current year then reset the sequence to 1
                        oCmd.CommandText = simpleSelect;
                        oCmd.ExecuteNonQuery();

                        oCmd.CommandText = getSubComponent;
                        var subFactor = Convert.ToInt32(oCmd.ExecuteScalar());

                        aleterCmd = string.Format(aleterCmd, subFactor);
                        oCmd.CommandText = aleterCmd;
                        oCmd.ExecuteNonQuery();

                        oCmd.CommandText = simpleSelect;
                        oCmd.ExecuteNonQuery();

                        oCmd.CommandText = resetIncCmd;
                        oCmd.ExecuteNonQuery();
                    }
                }
            }
        }

        public static int GetMaxTempActionNumber()
        {
            using (SqlConnection oCon = new SqlConnection(GetConnectionString()))
            {
                int seqNo = 0;

                using (SqlCommand oCmd = new SqlCommand())
                {
                    string cmd = "SELECT MAX(TO_NUMBER(SUBSTR(CODE, 6, 4))) MAX_CODE FROM ACTION WHERE CODE LIKE 'TEMP%'";

                    oCmd.Connection = oCon;
                    oCmd.CommandText = cmd;
                    oCmd.CommandType = System.Data.CommandType.Text;

                    oCon.Open();
                    var result = oCmd.ExecuteScalar();
                    seqNo = (result == DBNull.Value ? 0 : Convert.ToInt32(result));

                    oCmd.Dispose();
                    oCon.Close();

                    seqNo++;

                    return seqNo;
                }
            }
        }

        public static void LogMyError(Type pClassName, string pCustomMessage, string pURLString, Exception pEx)
        {
            //ILog logger = LogManager.GetLogger(pClassName);
            //ExceptionDetails ed = new ExceptionDetails();
            //ed.CustomMessage = pCustomMessage;
            //ed.URLString = pURLString;
            //logger.Error(ed, pEx);
        }
    }
}
